
interface obj {
  [k: string]: any;
}
interface options {
  strict: boolean;
  protoAction: "error" | "ignore";
  constructorAction: "error" | "ignore";
  bigintString: boolean;
}
type reviver = (this: any, key: string, value: any) => any;
type replacer = (number | string)[] | null;

export class tmkJSON {
  private static readonly escapee = { "\"": "\"", "\\": "\\", "/": "/", b: "\b", f: "\f", n: "\n", r: "\r", t: "\t", };
  private static readonly suspectProtoRx = /(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])/;
  private static readonly suspectConstructorRx = /(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)/;
  private static _reviver?: reviver;
  private static index: number; // 当前字符索引
  private static c: string; // 当前字符
  private static jsonString: string; // 待解析字符串

  private static readonly options: options = {
    strict: false, // 不严格意味着不会为“重复键”生成语法错误
    protoAction: 'error',
    constructorAction: 'error',
    bigintString: false
  };

  private static readonly escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
  private static readonly meta: obj = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\' };
  private static replacer?: reviver | replacer;
  private static gap: string;
  private static indent: string;
  private static quote(string: string) {
    // 如果字符串不包含 \t " \ , 就可以放心地在它周围加上一些引号。
    // 否则，我们还必须用安全转义序列替换违规字符。
    tmkJSON.escapable.lastIndex = 0;
    return tmkJSON.escapable.test(string) ? '"' + string.replace(tmkJSON.escapable, (a) => {
      let c = tmkJSON.meta[a];
      return typeof c === 'string'
        ? c
        : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
    }) + '"' : '"' + string + '"';
  }
  private static str(key: number | string, holder: obj): string {
    // 取字符串 holder[key].
    let i: number, k: number | string, v: any, length: number, mind = tmkJSON.gap, partial: any[], value: any = holder[key];
    // 如果该值有toJSON方法，则调用它以获取替换值。
    if (value && typeof value === 'object' &&
      typeof value.toJSON === 'function') {
      value = value.toJSON(key);
    }
    // 如果我们是用replacer函数调用的，那么就调用replacer来获取替换值。
    if (typeof tmkJSON.replacer === 'function') {
      value = tmkJSON.replacer.call(holder, key as string, value);
      // value = (tmkJSON.replacer as Function).call(holder, key, value);
    }
    const vt = typeof value;
    // 接下来的操作取决于值的类型。
    switch (vt) {
      case 'string':
        return tmkJSON.quote(value);
      case 'number': // JSON数字必须是有限的。将非有限数编码为空。
        return isFinite(value) ? String(value) : 'null';
      case 'boolean':
      case 'bigint':
        // case 'null': // 如果值类型是 boolean or null, 将其转换为字符串.
        // 注意: typeof null 不是 'null'. 这个案子被包括在这里，希望有一天能得到解决。
        return value.toString();
      case 'object': // 如果类型是 'object', 需要确定他是 object array null.
        if (!value) { // 由于ECMAScript中的一个规范错误，typeof null是“object”，所以请注意这种情况。
          return 'null';
        }
        // 创建一个数组来保存字符串化此对象值的部分结果。
        tmkJSON.gap += tmkJSON.indent;
        partial = [];
        if (Array.isArray(value)) { // 如果值是数组
          // 值是一个数组。把每个元素串起来。将null用作非JSON值的占位符。
          length = value.length;
          for (i = 0; i < length; i += 1) {
            partial[i] = tmkJSON.str(i, value) || 'null';
          }
          // 将所有元素连接在一起，用逗号分隔，并用[]括起来。
          v = partial.length === 0
            ? '[]'
            : tmkJSON.gap
              ? '[\n' + tmkJSON.gap + partial.join(',\n' + tmkJSON.gap) + '\n' + mind + ']'
              : '[' + partial.join(',') + ']';
          tmkJSON.gap = mind;
          return v;
        }
        // 如果replacer是数组，则使用它来选择要字符串化的成员。
        if (tmkJSON.replacer && typeof tmkJSON.replacer === 'object') {
          length = tmkJSON.replacer.length;
          for (i = 0; i < length; i += 1) {
            if (typeof tmkJSON.replacer[i] === 'string') {
              k = tmkJSON.replacer[i] as string;
              v = tmkJSON.str(k, value);
              if (v) {
                partial.push(tmkJSON.quote(k) + (tmkJSON.gap ? ': ' : ':') + v);
              }
            }
          }
        } else {
          // 否则，遍历对象中的所有键。
          Object.keys(value).forEach((k) => {
            let v = tmkJSON.str(k, value);
            if (v) {
              partial.push(tmkJSON.quote(k) + (tmkJSON.gap ? ': ' : ':') + v);
            }
          });
        }
        // 将所有成员文本连接在一起，用逗号分隔，并用 {} 包裹
        v = partial.length === 0
          ? '{}'
          : tmkJSON.gap
            ? '{\n' + tmkJSON.gap + partial.join(',\n' + tmkJSON.gap) + '\n' + mind + '}'
            : '{' + partial.join(',') + '}';
        tmkJSON.gap = mind;
        return v;
    }
    return "";
  }

  constructor (options?: options) {
    if (options) { Object.assign(tmkJSON.options, options); }
  }

  private static error(m: number | string) {
    // 出错时呼叫错误。
    throw {
      name: 'SyntaxError',
      message: m,
      index: tmkJSON.index,
      jsonString: tmkJSON.jsonString,
    };
  }

  private static next(c?: string): string {
    // 如果提供了c参数，请验证它是否与当前字符匹配。
    if (c && c !== tmkJSON.c) {
      tmkJSON.error("Expected '" + c + "' instead of '" + tmkJSON.c + "'");
    }
    // 获得下一个字符。当没有更多的字符时，返回空字符串。
    tmkJSON.c = tmkJSON.jsonString.charAt(tmkJSON.index++);
    // index++;
    return tmkJSON.c;
  }

  private static number(): number | bigint | string {
    // 解析数值。
    let string = '';
    if (tmkJSON.c === '-') {
      string = '-';
      tmkJSON.next('-');
    }
    while (tmkJSON.c >= '0' && tmkJSON.c <= '9') {
      string += tmkJSON.c;
      tmkJSON.next();
    }
    if (tmkJSON.c === '.') {
      string += '.';
      while (tmkJSON.next() && tmkJSON.c >= '0' && tmkJSON.c <= '9') {
        string += tmkJSON.c;
      }
    }
    if (tmkJSON.c === 'e' || tmkJSON.c === 'E') {
      string += tmkJSON.c;
      tmkJSON.c = tmkJSON.jsonString.charAt(tmkJSON.index++);
      if (tmkJSON.c === '-' || tmkJSON.c === '+') {
        string += tmkJSON.c;
        tmkJSON.next();
      }
      while (tmkJSON.c >= '0' && tmkJSON.c <= '9') {
        string += tmkJSON.c;
        tmkJSON.next();
      }
    }
    const v = +string;
    if (isFinite(v)) {
      return ((v > 2147483647 || v < -2147483648)) ? (tmkJSON.options.bigintString ? string : BigInt(string)) : v;
    }
    tmkJSON.error('Bad number');
    throw null;
  }

  private static string(): string {
    // 分析字符串值。
    let hex, i, string = '', uffff, sat = tmkJSON.index;
    // 解析字符串值时，必须查找 " and \  字符。
    if (tmkJSON.c === '"') {
      while (tmkJSON.next()) {
        if (tmkJSON.c === '"') {
          if (tmkJSON.index - 1 > sat) string += tmkJSON.jsonString.substring(sat, tmkJSON.index - 1);
          tmkJSON.next();
          return string;
        }
        if (tmkJSON.c === '\\') {
          if (tmkJSON.index - 1 > sat) string += tmkJSON.jsonString.substring(sat, tmkJSON.index - 1);
          tmkJSON.next();
          if (tmkJSON.c === 'u') {
            uffff = 0;
            for (i = 0; i < 4; i += 1) {
              hex = parseInt(tmkJSON.next(), 16);
              if (!isFinite(hex)) {
                break;
              }
              uffff = uffff * 16 + hex;
            }
            string += String.fromCharCode(uffff);
          } else if (typeof tmkJSON.escapee[tmkJSON.c] === 'string') {
            string += tmkJSON.escapee[tmkJSON.c];
          } else {
            break;
          }
          sat = tmkJSON.index;
        }
      }
    }
    tmkJSON.error('Bad string');
    throw null;
  }

  private static white() { // 跳过空白。
    while (tmkJSON.c && tmkJSON.c <= ' ') {
      tmkJSON.next();
    }
  }

  private static word() {
    // true, false, or null.
    switch (tmkJSON.c) {
      case 't':
        tmkJSON.next('t');
        tmkJSON.next('r');
        tmkJSON.next('u');
        tmkJSON.next('e');
        return true;
      case 'f':
        tmkJSON.next('f');
        tmkJSON.next('a');
        tmkJSON.next('l');
        tmkJSON.next('s');
        tmkJSON.next('e');
        return false;
      case 'n':
        tmkJSON.next('n');
        tmkJSON.next('u');
        tmkJSON.next('l');
        tmkJSON.next('l');
        return null;
    }
    tmkJSON.error("Unexpected '" + tmkJSON.c + "'");
    throw null;
  }

  private static array(): any[] {
    // 分析数组值。
    const array: any[] = [];
    if (tmkJSON.c === '[') {
      tmkJSON.next('[');
      tmkJSON.white();
      if (<string>tmkJSON.c === ']') {
        tmkJSON.next(']');
        return array; // empty array
      }
      while (tmkJSON.c) {
        array.push(tmkJSON.value());
        tmkJSON.white();
        if (<string>tmkJSON.c === ']') {
          tmkJSON.next(']');
          return array;
        }
        tmkJSON.next(',');
        tmkJSON.white();
      }
    }
    tmkJSON.error('Bad array');
    throw null;
  }

  private static object(): obj {
    // 分析对象值。
    let key;
    const obj: obj = {};
    if (tmkJSON.c === '{') {
      tmkJSON.next('{');
      tmkJSON.white();
      if (<string>tmkJSON.c === '}') {
        tmkJSON.next('}');
        return obj; // 空对象
      }
      while (tmkJSON.c) {
        key = tmkJSON.string();
        tmkJSON.white();
        tmkJSON.next(':');
        if (
          tmkJSON.options.strict === true &&
          Object.hasOwnProperty.call(obj, key)
        ) {
          tmkJSON.error('Duplicate key "' + key + '"');
        }

        if (tmkJSON.suspectProtoRx.test(key) === true) {
          if (tmkJSON.options.protoAction === 'error') {
            tmkJSON.error('Object contains forbidden prototype property');
          } else if (tmkJSON.options.protoAction === 'ignore') {
            tmkJSON.value();
          } else {
            obj[key] = tmkJSON.value();
          }
        } else if (tmkJSON.suspectConstructorRx.test(key) === true) {
          if (tmkJSON.options.constructorAction === 'error') {
            tmkJSON.error('Object contains forbidden constructor property');
          } else if (tmkJSON.options.constructorAction === 'ignore') {
            tmkJSON.value();
          } else {
            obj[key] = tmkJSON.value();
          }
        } else {
          obj[key] = tmkJSON.value();
        }

        tmkJSON.white();
        if (<string>tmkJSON.c === '}') {
          tmkJSON.next('}');
          return obj;
        }
        tmkJSON.next(',');
        tmkJSON.white();
      }
    }
    tmkJSON.error('Bad object');
    throw null;
  }

  private static value(): string | number | bigint | boolean | obj | null {
    // 解析JSON值。它可以是 an object, an array, a string, a number or a word.
    tmkJSON.white();
    switch (tmkJSON.c) {
      case '{':
        return tmkJSON.object();
      case '[':
        return tmkJSON.array();
      case '"':
        return tmkJSON.string();
      case '-':
        return tmkJSON.number();
      default:
        return tmkJSON.c >= '0' && tmkJSON.c <= '9' ? tmkJSON.number() : tmkJSON.word();
    }
  }

  private static reviver(key: string, value: any): any {
    let v: any, vl: any = value[key];
    if (vl && typeof vl === 'object') {
      Object.keys(vl).forEach((k) => {
        v = tmkJSON.reviver(k, vl);
        if (v !== undefined) {
          vl[k] = v;
        } else {
          delete vl[k];
        }
      });
    }
    return (tmkJSON._reviver as reviver).call(value, key, vl);
  }
  /** 解析JSONString
   * @参数 string:str 需要解析的JSONString
   * @参数 reviver:fun 需要解析的JSONString
   * @返回 Object:解析后的对象
   */
  public static parse(text: string, reviver?: reviver): any {
    tmkJSON.index = 0; // 当前字符索引
    tmkJSON.c = " "; // 当前字符
    tmkJSON.jsonString = text;

    const ret = tmkJSON.value();
    tmkJSON._reviver = reviver;
    if (typeof reviver == "function") {
      return tmkJSON.reviver("", { "": ret });
    }
    return ret;
  }
  /** 序列化 value
   * @参数 value 将要转为JSONString的值
   * @参数 replacer 序列化的key值数组或函数
   * @参数 space 缩进空白字符或空格字符数
   * @返回 序列化好的 JSONString
   */
  public static stringify(value: any, replacer?: reviver | replacer, space?: string | number): any {
    tmkJSON.gap = '';
    tmkJSON.indent = '';
    // 如果 space 参数是一个数字，则生成一个包含 space个 的缩进字符串。
    if (typeof space === 'number') { for (let i = 0; i < space; i++) { tmkJSON.indent += ' '; } }
    else if (typeof space === 'string') { tmkJSON.indent = space; }// 如果 space 参数是一个字符串，它将用作缩进字符串.
    // 如果有 replacer，则必须是函数或数组。否则，抛出一个错误。
    if (replacer && !(replacer instanceof Function || Array.isArray(replacer))) {
      throw new Error('JSON.stringify 参数 replacer 必须是 数组或函数');
    }
    tmkJSON.replacer = replacer;
    // 在 "" 键下创建一个包含我们的值的假根对象. 返回字符串化值的结果.
    return tmkJSON.str("", { "": value });
  }
}
export default tmkJSON;